package com.dude.search;

import java.util.List;

import org.opencv.core.Size;
import org.opencv.highgui.Highgui;
import org.opencv.highgui.VideoCapture;

import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

/**
 * this class handles the visual capture of the camera
 */
public class CameraView extends SurfaceView implements SurfaceHolder.Callback, Runnable
{
	// for debugging
	private static final String TAG = "camera view";
	
	// --------------- camera associated parameters --------------
	private SurfaceHolder holder;
	private VideoCapture camera;
	private Thread camThread;
	
	protected int mFrameHeight;
	protected int mFrameWidth;

	private Canvas canvas;

	private Matrix matrix;

	private Bitmap bmp;
	protected boolean finished = false;
	    
	// --------------- constructor ---------------------
	public CameraView(Context context) {
		super(context);
		holder = getHolder();
        holder.addCallback(this);
	}

	//================================================
	//video handling methods
	//================================================
	
	// --------------------- camera initialization ------------------
	@Override
	public void surfaceCreated(SurfaceHolder holder) 
	{
		Log.i(TAG, "surfaceCreated");
	    camera = new VideoCapture(Highgui.CV_CAP_ANDROID);
	    if (camera.isOpened()) 
	    {
	    	camThread = new Thread(this);
	    	camThread.start();
        } else {
            camera.release();
            camera = null;
            Log.e(TAG, "Failed to open native camera");
        }
	}
	
	// ------------- called when screen resolution changes -------------------------
	@Override
	public void surfaceChanged(SurfaceHolder holder, int format, int width,	int height)
	{
		Log.i(TAG, "surfaceChanged");
        synchronized (this) {
            if (camera != null && camera.isOpened()) {
                Log.i(TAG, "before camera.getSupportedPreviewSizes()");
                List<Size> sizes = camera.getSupportedPreviewSizes();
                Log.i(TAG, "after camera.getSupportedPreviewSizes()");
                mFrameWidth = width;
                mFrameHeight = height;

                // selecting optimal camera preview size
                {
                    double minDiff = Double.MAX_VALUE;
                    for (Size size : sizes) {
                        if (Math.abs(size.height - height) < minDiff) {
                            mFrameWidth = (int) size.width;
                            mFrameHeight = (int) size.height;
                            minDiff = Math.abs(size.height - height);
                        }
                    }
                }

                camera.set(Highgui.CV_CAP_PROP_FRAME_WIDTH, mFrameWidth);
                camera.set(Highgui.CV_CAP_PROP_FRAME_HEIGHT, mFrameHeight);
            }
        }
	}

	// ---------------------- camera disposal --------------------------
	@Override
	public void surfaceDestroyed(SurfaceHolder holder)
	{
		Log.i(TAG, "surfaceDestroyed");
        if (camera != null) 
        {
            synchronized (this)
            {
            	if (camera != null)
            	{
	                camera.release();
	                camera = null;
	                Log.i(TAG, "camera released");
            	}
            	
            	if (camThread != null && camThread.isAlive())
    				try {
    					camThread.join(3000);
    				} catch (InterruptedException e) {
    					Log.e(TAG, e.getMessage());
    				}
            }
        }
	}

	
	// ------------------- main loop method -----------------------
	@Override
	public void run()
	{
		Log.i(TAG, "started camera thread");
		
		// need to let the camera thread and the UI thread to sync - 3 seconds are enough
		try 
    	{
    		Log.i(TAG, "yielding cam thread");
			Thread.sleep(3000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
    	Log.i(TAG, "yielding stopped");
		
        while (true) 
        {
            bmp = null;

            synchronized (this) 
            {
                if (camera == null)
                {
                	Log.e(TAG, "camera is null");
                    break;
                }

                if (!camera.grab()) {
                    Log.e(TAG, "mCamera.grab() failed");
                    break;
                }
                
            	if (finished)
            	{
            		camera.release();
            		camera = null;
                    Log.i(TAG, "camera released");
                    break;
            	}

//                bmp = processFrameMultiPic(camera);
                
                bmp = processFrame(camera);
                
                if (bmp != null) 
                {
                    canvas = holder.lockCanvas();
                    if (canvas != null) {
                        //canvas.drawBitmap(bmp, (canvas.getWidth() - bmp.getWidth()) / 2, (canvas.getHeight() - bmp.getHeight()) / 2, null);
                        matrix = new Matrix();
                        matrix.preTranslate((canvas.getWidth() - bmp.getWidth()) / 2, (canvas.getHeight() - bmp.getHeight()) / 2);
                        
                        if(getResources().getConfiguration().orientation ==  Configuration.ORIENTATION_PORTRAIT)
                        	matrix.postRotate(90f,(canvas.getWidth()) / 2,(canvas.getHeight()) / 2);
                        
                        canvas.drawBitmap(bmp, matrix, new Paint());
                        holder.unlockCanvasAndPost(canvas);
                    }
                    bmp.recycle();
                }
            }
            
        }

        Log.i(TAG, "Finished camera thread");
	}

	protected Bitmap processFrame(VideoCapture capture) 
	{
		return null;
	}
	
	protected Bitmap processFrameMultiPic(VideoCapture v){
		return null;
	};

}
